/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: wx
 * Date: 2024-03-10
 * Time: 10:46
 */

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**文件内容操作
 * 涉及到关键概念流  流比较抽象. 它指的是操作系统提供的概念
 * c/java才能够基于流进行封装
 *
 * 水流的特点,具体一点
 * 要接100ml的水 一次借多少毫升的水,分几次接
 * 因此可以有不同种的组合方式
 *
 * 文件流也是,类似的情况
 * 比如要读写 100 字节的数据
 * 可以分几次读,一次读多少字节
 * 变化多样,但是最终得到的效果是一致的
 *
 * java标准库对于流进行了一系列的封装.提供了一组类来负责这些工作
 * 针对这么多类,可以分成两个大类
 *
 * 字节流
 * 以字节为单位进行读写.一次最少读写一个字节
 *
 * 字符流
 * 以字符为单位进行读写.比如 如果是utf8表示汉字,3个字节就是一个汉字,每次读写都得以 3 个汉字为单位(一个汉字单位)来进行读写,不能一次读写半个汉字
 */
public class IODemo7 {
    public static void main(String[] args) throws IOException {
        try (InputStream inputStream = new FileInputStream(("aaa/test.txt"))) {
            //读文件
            //也不知道有多少文件,使用while
            while (true) {
              byte[] buffer = new byte[1024];
              int n = inputStream.read(buffer);
                if (n == -1) {
                    break;
                }
                for (int i = 0; i < n; i++) {
                    System.out.printf("%x ",buffer[i]);
                }
            }
        }
        //访问硬盘是低效操作
        //访问内存是高效操作
        // 此时就是希望借助内存来减少读写硬盘的次数
        // 此时对应的这个内存就被称为"缓冲区"buffer

    }
    public static void main6(String[] args) throws IOException {
        try (InputStream inputStream = new FileInputStream(("aaa/test.txt"))) {
            //读文件
            //也不知道有多少文件,使用while
            while (true) {
                int b = inputStream.read();
                if (b == -1) {
                    //文件读取完毕
                    break;
                }
                //打印字节的数据
                System.out.printf("%x ", b);
            }
        }
    }

    public static void main5(String[] args) {
        try(InputStream inputStream = new FileInputStream("./test2.txt")) {
            byte[] buffer = new byte[1024];
            inputStream.read(buffer ,2,5);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
//第二个第三个版本的read 返回的int表示实际读取的字节个数
// 默认情况下,read会尝试把数组填满,但文件的实际剩余长度可能不足以填满
// 返回值就可以告诉我们实际是填充了多少字节
    }

    public static void main4(String[] args) {
        try(InputStream inputStream = new FileInputStream("./test2.txt")) {
            byte[] buffer = new byte[1024];
          //  buffer 是计算机中常见的术语,表示"缓冲区",往往是一个内存空间

            inputStream.read(buffer);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main2(String[] args) {
        try(InputStream inputStream = new FileInputStream("./test2.txt")) {
            inputStream.read();//无参数版本 每次调用都会读取一个字节,返回值就表示读取到的这个字节的值 看起来里边是一个 int 实际上是一个 byte
                               //实际取值为 0-255 此处还有一个特殊情况,如果读取到文件末尾,继续进行 read 就会返回 -1;
            //inputStream.read(//byte[] b);一个参数版本,传入的是字节数组参数,是一个"输出型参数"
                                   //byte[] 是引用类型,方法内部针对数组内容进行修改
                                   //方法执行结束之后,方法外部也会生效
               // 使用的时候,把空的数组交给read,read内部对数组进行内容进行填充
        } catch (IOException e) {
            throw new RuntimeException(e);
        }//这个代码就是把文件的资源释放交给了 try 一旦代码出了 try这样的代码块,此时try就帮我们自动调用 InputStream 的close
    }

    public static void main1(String[] args) throws IOException {

       /* //打开文件
        InputStream inputStream = new FileInputStream("./test.txt");  //InputStream是一个用抽象类修饰的构造方法,不能进行实例化
                                   // FileInputStream()指的是从文件读取 创建对象的过程就是打开文件的过程
        //关闭文件
        inputStream.close(); //  直接这么写,很可能执行不到,中间的逻辑问题出现return或者抛出异常close就都执行不到了
        */

        InputStream inputStream = null;
        try{
            //打开文件
             inputStream = new FileInputStream("./test2.txt");//放在这里无法被读取到,需要在开头重写  inputStream
        } finally {
            //关闭文件
            inputStream.close();
        }
        //java 还提供了一个版本,try with resource
     }

}



//关闭文件可以理解成为释放了文件的相关资源
//进程 => PCB(一个或者多个) -> 文件描述符表 ,记录了当前打开那些文件
//                             顺序表或者数组
//文件资源泄露       //   (数组中的每个元素都是一个结构体,这个结构体就具体描述了你打开的文件在文件系统上的一些属性)
//                    每次打开文件都是需要在文件描述符表中占据一个位置,如果不关闭的话,还一直打开就会使文件描述符表被耗尽
//                   (文件描述表的长度是有上限的)  文件描述符表被耗尽之后,后续再打开文件就会打开失败,
//   进一步就会引起后续一系列的逻辑问题
//那么为什么不进行自动扩容呢?
//对于操作系统内核来说,对于性能的要求很高,要想进行自动扩容操作,得慎重.扩容容易引起系统卡顿,得不偿失

